#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <utility>
#include <stdexcept>

#include "scopeguard.h"
#include "bytearray.h"

#ifdef COMMON_GTEST
#include <gtest/gtest.h>
#endif

using std::runtime_error;

#define THROW(x) throw std::runtime_error(#x);

void* safe_memcpy(void *dst,const void *src,size_t size)
{
    if (not_overlap(dst,size,src,size))
        memcpy(dst,src,size);
    else if (dst != src)
        memmove(dst,src,size);
    return dst;
}

void RawByteArray::resize(size_t size)
{
    if (size <= size_) {
        size_ = size;
    } else if (size > capacity_){
        THROW(ERANGE);
    } else {
        memset(ptr_ + size_,0,size - size_);
        size_ = size;
    }
    return ;
}

RawByteArray::RawByteArray(void *buf,size_t capacity,const void *src,size_t size):
    ptr_(static_cast<uchar*>(buf)),
    capacity_(capacity)
{
    if (capacity_ < size)
        THROW(ERANGE);
    size_ = size;
    safe_memcpy(ptr_,src,size);
    return ;
}

RawByteArray& RawByteArray::append(const void *src,size_t size)
{
    size_t new_size = size_ + size;
    if (new_size > capacity_)
        THROW(ERANGE);
    safe_memcpy(ptr_ + size_,src,size);
    size_ = new_size;
    return *this;
}

int ByteArray::compare(const void *ptr,size_t s) const
{
    size_t l_size = size();
    if (ptr == nullptr)
        s = 0;

    int memcmp_ret = 0;
    size_t min_size = std::min(l_size,s);
    if (min_size != 0) {
        memcmp_ret = memcmp(data(),ptr,min_size);
        if (memcmp_ret != 0)
            return memcmp_ret;
    }

    if (l_size == s)
        return 0;
    if (l_size < s)
        return -1;
    return 1;
}

ByteArray& ByteArray::appendFormat(const char *f,...)
{
    if (f != nullptr && *f != '\0') {
        va_list ap;
        va_start(ap,f);
        ON_SCOPE_EXIT {
            va_end(ap);
        };
        appendVFormat(f,ap);
    }
    return *this;
}

ByteArray& ByteArray::appendVFormat(const char *f,va_list ap)
{
    if (f != nullptr && *f != '\0') {
        va_list ap1;
        va_copy(ap1,ap);
        ON_SCOPE_EXIT {
            va_end(ap1);
        };

        constexpr size_t buf_size = 256;
        char buf[buf_size];
        int ret = vsnprintf(buf,buf_size,f,ap);
        if (ret < 0)
            throw runtime_error("ByteArray::appendVFormat(): vsnprintf(): fail");

        if (static_cast<size_t>(ret) < buf_size) {
            append(buf,ret);
        } else { // 表明 buf 空间不足.
            size_t old_size = size();
            resize(old_size + ret + 1);

            ret = vsnprintf(data() + old_size,ret + 1,f,ap1);
            if (ret < 0)
                throw runtime_error("ByteArray::appendVFormat(): vsnprintf(): fail");
            resize(old_size + ret);
        }
    }
    return *this;
}

ByteArray ByteArray::format(const char *f,...)
{
    if (f != nullptr && *f != '\0') {
        va_list ap;
        va_start(ap,f);
        ON_SCOPE_EXIT {
            va_end(ap);
        };
        return vformat(f,ap);
    }
    return (ByteArray());
}

////////////Other Implement Begin///////////////////////////////////////////////

////////////Other Implement End  ///////////////////////////////////////////////

////////////ByteArray TestCase Begin////////////////////////////////////////////

#ifdef COMMON_GTEST

namespace /* lineTraverse-GTEST */{

struct LineTraverseTest : public ::testing::Test {
    void SetUp() override;

    void increase();
    size_t counter() const;
private:
    size_t traverseCounter;
};

inline void LineTraverseTest::increase()
{
    ++traverseCounter;
}

inline size_t LineTraverseTest::counter() const
{
    return traverseCounter;
}

void LineTraverseTest::SetUp()
{
    traverseCounter = 0;
}

inline void expect_eq(const char *l,const char *p,size_t s)
{
    std::string left {l};
    std::string right {p,s};
    EXPECT_EQ(left,right);
}

TEST_F(LineTraverseTest,Test1) {
    lineTraverse(nullptr,0,[&] (const char*,size_t) -> LineTraverseReturn {
        increase();
        return LineTraverseReturn::CONTINUE;
    });

    EXPECT_EQ(0,counter());
}

TEST_F(LineTraverseTest,Test2) {
    lineTraverse("\n",[&] (const char *p,size_t s) -> LineTraverseReturn {
        EXPECT_EQ(0,s);
        increase();
        return LineTraverseReturn::CONTINUE;
    });

    EXPECT_EQ(1,counter());
}

TEST_F(LineTraverseTest,Test3) {
    lineTraverse("hello",[&] (const char *p,size_t s) -> LineTraverseReturn {
        expect_eq("hello",p,s);
        increase();
        return LineTraverseReturn::CONTINUE;
    });

    EXPECT_EQ(1,counter());
}

TEST_F(LineTraverseTest,Test4) {
    lineTraverse("\n\n\n",[&] (const char *p,size_t s) -> LineTraverseReturn {
        EXPECT_EQ(0,s);
        increase();
        return LineTraverseReturn::CONTINUE;
    });

    EXPECT_EQ(3,counter());
}

TEST_F(LineTraverseTest,Test5) {
    lineTraverse("hello\nworld",[&] (const char *p,size_t s) -> LineTraverseReturn{
        switch (counter()) {
        case 0:
            expect_eq("hello",p,s);
            break;
        case 1:
            EXPECT_EQ(0,s);
            break;
        case 2:
            expect_eq("world",p,s);
            break;
        default:
            ADD_FAILURE() << "遍历超出范围";
        }

        increase();
        return LineTraverseReturn::CONTINUE;
    });

    EXPECT_EQ(3,counter());
}

TEST_F(LineTraverseTest,Test6) {
    lineTraverse("\na\n\nb\n\n\nc",[&] (const char *p,size_t s) -> LineTraverseReturn {
        switch (counter()) {
        case 0:
            EXPECT_EQ(0,s);
            break;
        case 1:
            expect_eq("a",p,s);
            break;
        case 2:
            EXPECT_EQ(0,s);
            break;
        case 3:
            EXPECT_EQ(0,s);
            break;
        case 4:
            expect_eq("b",p,s);
            break;
        case 5:
            EXPECT_EQ(0,s);
            break;
        case 6:
            EXPECT_EQ(0,s);
            break;
        case 7:
            EXPECT_EQ(0,s);
            break;
        case 8:
            expect_eq("c",p,s);
            break;
        default:
            ADD_FAILURE() << "遍历超出范围";
        }

        increase();
        return LineTraverseReturn::CONTINUE;
    });

    EXPECT_EQ(9,counter());
}

} // lineTraverse-GTEST

#endif

////////////ByteArray TestCase End  ////////////////////////////////////////////
